#include "epoll.h"

Epoll *Epoll::EpollInstance = nullptr;
Pool  *Epoll::pool          = nullptr;


static void epoll_task_fun(void* arg)
{

    Epoll* epoll = Epoll::GetInstance();
    char read_buf[1024] ={0};

    int fd = *(int*)arg;

    epoll->Printf_Info(fd);

    // 有数据可读
    ssize_t nread = read(fd, (void*)read_buf, sizeof(read_buf));
    if (nread > 0)
    {
        read_buf[nread] = '\0'; // 以防没有'\0'

        write(fd,read_buf,strlen(read_buf));

        std::cout << "Received data: " << read_buf << std::endl;
        
    } else if (nread == 0) {
        // 客户端关闭连接
        std::cout << "Client closed fd: " << fd << std::endl;
        epoll->Del_Events(fd);
        close(fd);
    } else {
        perror("read");
        // 出错处理
        epoll->Del_Events(fd);
        close(fd);
    }


}

/**
 * @brief   打印ip结构体信息
 * 
 * @param info struct sockaddr_in　结构体
 */
void Epoll::Printf_Info(struct sockaddr_in& info)const
{

    char buf[INET6_ADDRSTRLEN] = {0};
    inet_ntop(info.sin_family, &info.sin_addr.s_addr,buf, sizeof(buf));
    cout << "family: " << (info.sin_family == AF_INET ? " ip4" : "ip6") << endl;
    cout << "ip: " << buf << endl;
    cout << "port: " << ntohs(info.sin_port) << endl;

}
/**
 * @brief 打印ip结构体信息
 * 
 * @param fd   文件描述符
 */
void Epoll::Printf_Info(int fd)const
{
    struct sockaddr_storage info_addr;
    socklen_t addr_len = sizeof(info_addr);
    char ip[INET6_ADDRSTRLEN];
    int port;

    // 获取对端地址
    if (getpeername(fd, (struct sockaddr*)&info_addr, &addr_len) == -1) {
        perror("getpeername");
        return;
    }

    // 解析地址信息
    if (info_addr.ss_family == AF_INET) {
        struct sockaddr_in* p = (struct sockaddr_in*)&info_addr;
        if (inet_ntop(AF_INET, &p->sin_addr, ip, sizeof(ip)) == NULL) {
            perror("inet_ntop");
            return;
        }
        port = ntohs(p->sin_port);
    } else if (info_addr.ss_family == AF_INET6) {
        struct sockaddr_in6* p = (struct sockaddr_in6*)&info_addr;
        if (inet_ntop(AF_INET6, &p->sin6_addr, ip, sizeof(ip)) == NULL) {
            perror("inet_ntop");
            return;
        }
        port = ntohs(p->sin6_port);
    } else {
        cerr << "Unsupported address family" << endl;
        return;
    }

    // 获取套接字类型
    int sock_type;
    socklen_t sock_type_len = sizeof(sock_type);
    if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &sock_type, &sock_type_len) == -1) {
        perror("getsockopt SO_TYPE");
        return;
    }

    // 获取套接字协议
    int protocol;
    socklen_t protocol_len = sizeof(protocol);
    if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &protocol_len) == -1) {
        perror("getsockopt SO_PROTOCOL");
        return;
    }

    cout << "Connection info:" << endl;
    cout << "FD: " << fd << endl;
    cout << "IP: " << ip << endl;
    cout << "Port: " << port << endl;
    cout << "Socket Type: " << sock_type << endl;
    cout << "Protocol: " << (protocol == AF_INET ? " ip4" : "ip6") << endl;
    cout << endl;



}

void Epoll::Bind(in_addr_t addr,in_port_t port,sa_family_t family)
{   
    /**
     * @brief 参数说明
     * 
     * int __domain：
     *              PF_INET /  AF_INET     Ipv4 网络协议
                    PF_INET6/AF_INET6     Ipv6 网络协议
     * int __type：

     * int __protocol：
                    一般为零
     */
    len = sizeof(servaddr);
    this->sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(this->sockfd == -1)
    {
        cout << "Failed to allocate bytes" << endl;
        exit(1);
    }
    // 设置ip 复用
    int optval = 1;
    setsockopt(this->sockfd, SOL_SOCKET, SO_REUSEADDR,&optval, sizeof(optval));
    setsockopt(this->sockfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
   

    memset((void*)&servaddr,0,sizeof(servaddr));
    servaddr.sin_family         = family,
    servaddr.sin_addr.s_addr    = addr;
    servaddr.sin_port           = port;

    res = bind(this->sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
    if(res == -1)
    {
        // 在bind失败后添加：
        std::cerr << "bind failed: " << strerror(errno) << " (errno=" << errno << ")" << std::endl;
        cout << "bind fail" << endl;
        exit(1);
    }
    res = listen(this->sockfd, 20);
    if(res == -1)
    {
        cout << "listen fail" << endl;
        exit(1);
    }
    Printf_Info(servaddr);
}

/**
 * @brief Construct a new Epoll:: Epoll object
 * 
 * @param addr      地址
 * @param port      端口
 * @param family    协议
 */
Epoll::Epoll(in_addr_t addr,in_port_t port,sa_family_t family)
{

    // 初始化线程池
    pool = Pool::GetInstance(10,1024,100,2);
    // 申请内存资源
    epbuf = new struct epoll_event[ep_size];
    if(epbuf == nullptr)
    {
        cout << " new epbuf fail" << endl;
        exit(1);
    }
    // 绑定ip prot
    this->Bind(addr,port,family);

    // 绑定任务处理函数
    pool->Set_TaskFun(epoll_task_fun);

}

/**
 * @brief           获得实列
 * @param addr      地址
 * @param port      端口
 * @param family    协议
 * @return Epoll& 
 */
Epoll* Epoll::GetInstance(string addr,uint16_t port, sa_family_t family)
{

    if (EpollInstance == nullptr) 
    {
        in_addr_t Netaddr = inet_addr(addr.c_str());
        in_port_t Netport = htons(port);
        EpollInstance = new Epoll(Netaddr,Netport,family);
    }

    return EpollInstance;

}

Epoll::~Epoll()
{
    cout << "释放资源" << endl;
    delete[] epbuf;
    delete EpollInstance;
    
}



/**
 * @brief  监听io
 * 
 */
void Epoll::ListensEvents(void)
{
    //创建一颗监听红黑树
    epollfd = epoll_create(this->ep_size);
    if(epollfd == -1)
    {
        cout << "epoll_create fail" << endl;
        exit(1);
    }

    Add_Events(this->sockfd, EPOLLIN | EPOLLET);

    while(1)
    {

        int retfds = epoll_wait(epollfd, epbuf, ep_size, -1);
        for(int i = 0; i < retfds; i++)
        {
            int cur_fd = epbuf[i].data.fd;
            //如果是监听fd，加入到epoll
            if(cur_fd == this->sockfd && (epbuf[i].events & EPOLLIN))
            {
                int acceptfd;
                socklen_t len;

                while((acceptfd = accept(this->sockfd, (struct sockaddr *) &cliaddr, &len)) > 0)
                {
                    this->Printf_Info(acceptfd);
                    Add_Events(acceptfd, EPOLLIN | EPOLLET);                   
                }
        
            }
            //如果是其他fd，push_tbool Events_Handled(struct epoll_event& epevent);ask，交给handler处理
            else if(epbuf[i].events & EPOLLIN)
            {
                Events_Handled(epbuf[i]);
            } 
        }
    }



}




/**
 * @brief           监听事件
 * 
 * @param fd        文件描述符
 * @param status    状态
 * @return true 
 */
bool Epoll::Add_Events(int fd, int status)
{
    /*
        Epoll events 有以下：

        EPOLLIN ：    表示对应的文件描述符可以读（包括对端SOCKET正常关闭）
        EPOLLOUT：    表示对应的文件描述符可以写
        EPOLLPRI：    表示对应的文件描述符有紧急的数据可读（这里应该表示有带外数据到来）
        EPOLLERR：    表示对应的文件描述符发生错误
        EPOLLHUP：    表示对应的文件描述符被挂断；
        EPOLLET：     将EPOLL设为边缘触发(Edge Triggered)模式，这是相对于水平触发(Level Triggered)而言的
        EPOLLONESHOT：只监听一次事件，当监听完这次事件之后，如果还需要继续监听这个socket的话，需要再次把这个socket加入到EPOLL队列里
    */
    memset((void*)&this->midep,0,sizeof(this->midep));
    this->midep.data.fd = fd;
    this->midep.events = status;
    //设置文件为非阻塞
    int oldoption = fcntl(fd, F_GETFL, 0);
    int newoption = oldoption | O_NONBLOCK;
    fcntl(fd, F_SETFL, newoption);

    res = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &this->midep);

    if (res == -1) {
        // 输出错误信息
        switch (errno)
        {
            case EBADF:
                std::cerr << "Error: epoll_ctl EBADF - invalid epoll file descriptor" << std::endl;
                break;
            case EEXIST:
                std::cerr << "Error: epoll_ctl EEXIST - fd already added" << std::endl;
                break;
            case EINVAL:
                std::cerr << "Error: epoll_ctl EINVAL - invalid op or event" << std::endl;
                break;
            case ENOMEM:
                std::cerr << "Error: epoll_ctl ENOMEM - insufficient memory" << std::endl;
                break;
            case ENOSPC:
                std::cerr << "Error: epoll_ctl ENOSPC - limit reached" << std::endl;
                break;
            default:
                std::cerr << "epoll_ctl error (" << errno << "): " << strerror(errno) << std::endl;
                break;
        }
        return false;
    }
    return true;

}

/**
 * @brief           删除监听事件
 * 
 * @param fd        文件描述符号
 * @return true 
 * @return false 
 */
bool Epoll::Del_Events(int fd)
{

    //将对应缓冲区下标标记为可用
    res = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);

    if(res == 0)
        close(fd);
    else
    {
        // 输出错误信息
        switch (errno)
        {
            case EBADF:
                std::cerr << "Error: epoll_ctl EBADF - invalid epoll file descriptor" << std::endl;
                break;
            case EEXIST:
                std::cerr << "Error: epoll_ctl EEXIST - fd already added" << std::endl;
                break;
            case EINVAL:
                std::cerr << "Error: epoll_ctl EINVAL - invalid op or event" << std::endl;
                break;
            case ENOMEM:
                std::cerr << "Error: epoll_ctl ENOMEM - insufficient memory" << std::endl;
                break;
            case ENOSPC:
                std::cerr << "Error: epoll_ctl ENOSPC - limit reached" << std::endl;
                break;
            default:
                std::cerr << "epoll_ctl error (" << errno << "): " << strerror(errno) << std::endl;
                break;
        }
        return false;
    }
    return true;

}

/**
 * @brief           事件操作句柄函数
 * 
 * @param epevent 
 * @return true 
 * @return false 
 */
bool Epoll::Events_Handled(struct epoll_event& epevent)
{
    bool flag ;
    /*
        Epoll events 有以下：
        EPOLLIN ：    表示对应的文件描述符可以读（包括对端SOCKET正常关闭）
        EPOLLOUT：    表示对应的文件描述符可以写
        EPOLLPRI：    表示对应的文件描述符有紧急的数据可读（这里应该表示有带外数据到来）
        EPOLLERR：    表示对应的文件描述符发生错误
        EPOLLHUP：    表示对应的文件描述符被挂断；
        EPOLLET：     将EPOLL设为边缘触发(Edge Triggered)模式，这是相对于水平触发(Level Triggered)而言的
        EPOLLONESHOT：只监听一次事件，当监听完这次事件之后，如果还需要继续监听这个socket的话，需要再次把这个socket加入到EPOLL队列里
    */
    switch (epevent.events)
    {
        case EPOLLIN:
        {   

            //添加到线程中
            flag = pool->Add_Task(epevent.data.fd);

            if(flag == false)
            {
                pool->Set_MaxTask(pool->Get_MaxTask()+20);
                pool->Add_Task(epevent.data.fd);
            }

            break;
        }

        case EPOLLHUP:
        {
            std::cout << "Connection hang up, fd: " << epevent.data.fd << std::endl;
            Del_Events(epevent.data.fd);
            close(epevent.data.fd);
            return false;
        }

        case EPOLLERR:
        {
            int err;
            socklen_t len = sizeof(err);
            if (getsockopt(epevent.data.fd, SOL_SOCKET, SO_ERROR, &err, &len) == -1) {
                perror("getsockopt");
            } else {
                std::cerr << "Socket error: " << strerror(err) << std::endl;
            }
            Del_Events(epevent.data.fd);
            close(epevent.data.fd);
            return false;
        }

        default:
        {   
            Del_Events(epevent.data.fd);
            std::cout << "Other events: " << epevent.events << std::endl;
            break;
        }
    }

    return true;
}



